/*
 * The contents of this web application are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this web application except in compliance with 
 * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the License.
 * 
 * The Original Code is owned by and the Initial Developer of the Original Code is 
 * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
 * 
 * Section 11 of the License is EXPRESSLY amended to include a provision stating 
 * that any dispute, including but not limited to disputes related to the enforcement 
 * of the License, to which Composite A/S as owner of the Original Code, as Initial 
 * Developer or in any other role, becomes a part to shall be governed by Danish law 
 * and be initiated before the Copenhagen City Court ("K�benhavns Byret")            
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Composite.C1Console.Events;
using Composite.Core.Logging;


namespace Composite.Data.Types.StoreIdFilter.Foundation
{
    internal static class StoreIdFilterQueryableCache
    {
        private static Dictionary<Type, Dictionary<Type, MethodInfo>> _storeIdFilterQueryableCreateQueryCache = new Dictionary<Type, Dictionary<Type, MethodInfo>>();
        private static Dictionary<Type, Dictionary<Type, MethodInfo>> _storeIdFilterQueryableExecuteMethodInfoCache = new Dictionary<Type, Dictionary<Type, MethodInfo>>();
        private static Dictionary<Type, MethodInfo> _storeIdFilterQueryableGetEnumeratorMethodInfoCache = new Dictionary<Type, MethodInfo>();


        static StoreIdFilterQueryableCache()
        {
            GlobalEventSystemFacade.SubscribeToFlushEvent(OnFlush);
        }



        public static MethodInfo GetStoreIdFilterQueryableCreateQueryMethodInfo(Type genericType, Type expressionType)
        {
            Dictionary<Type, MethodInfo> methodInfoes;

            if (_storeIdFilterQueryableExecuteMethodInfoCache.TryGetValue(genericType, out methodInfoes) == false)
            {
                methodInfoes = new Dictionary<Type, MethodInfo>();

                _storeIdFilterQueryableExecuteMethodInfoCache.Add(genericType, methodInfoes);
            }


            MethodInfo methodInfo;

            if (methodInfoes.TryGetValue(expressionType, out methodInfo) == false)
            {
                Type type = typeof(StoreIdFilterQueryable<>).MakeGenericType(new Type[] { genericType });

                methodInfo =
                    (from method in type.GetMethods()
                     where method.Name == "CreateQuery" &&
                           method.IsGenericMethod 
                     select method).First();

                methodInfo = methodInfo.MakeGenericMethod(new Type[] { expressionType });

                methodInfoes.Add(expressionType, methodInfo);
            }

            return methodInfo;
        }



        public static MethodInfo GetStoreIdFilterQueryableExecuteMethodInfo(Type genericType, Type expressionType)
        {
            Dictionary<Type, MethodInfo> methodInfoes;

            if (_storeIdFilterQueryableExecuteMethodInfoCache.TryGetValue(genericType, out methodInfoes) == false)
            {
                methodInfoes = new Dictionary<Type, MethodInfo>();

                _storeIdFilterQueryableExecuteMethodInfoCache.Add(genericType, methodInfoes);
            }


            MethodInfo methodInfo;

            if (methodInfoes.TryGetValue(expressionType, out methodInfo) == false)
            {
                Type type = typeof(StoreIdFilterQueryable<>).MakeGenericType(new Type[] { genericType });

                methodInfo =
                    (from method in type.GetMethods()
                     where method.Name == "Execute" &&
                           method.IsGenericMethod 
                     select method).First();

                methodInfo = methodInfo.MakeGenericMethod(new Type[] { expressionType });

                methodInfoes.Add(expressionType, methodInfo);
            }

            return methodInfo;
        }



        public static MethodInfo GetStoreIdFilterQueryableGetEnumeratorMethodInfo(Type genericType)
        {
            MethodInfo methodInfo;

            if (_storeIdFilterQueryableGetEnumeratorMethodInfoCache.TryGetValue(genericType, out methodInfo) == false)
            {
                Type type = typeof(StoreIdFilterQueryable<>).MakeGenericType(new Type[] { genericType });

                methodInfo =
                    (from method in type.GetMethods()
                     where method.Name == "GetEnumerator"
                     select method).First();

                _storeIdFilterQueryableGetEnumeratorMethodInfoCache.Add(genericType, methodInfo);
            }

            return methodInfo;
        }



        private static void Flush()
        {
            _storeIdFilterQueryableCreateQueryCache = new Dictionary<Type, Dictionary<Type, MethodInfo>>();
            _storeIdFilterQueryableGetEnumeratorMethodInfoCache = new Dictionary<Type, MethodInfo>();
            _storeIdFilterQueryableExecuteMethodInfoCache = new Dictionary<Type, Dictionary<Type, MethodInfo>>();
        }



        private static void OnFlush(FlushEventArgs args)
        {
            Flush();
        }
    }
}
